
{***********************************************************}
{                                                           }
{      Luxena dbExpress eXtension Components                }
{                                                           }
{      TDBXQuery component                                  }
{                                                           }
{      Copyright (c) 2001-2006 Luxena Software Company      }
{      http://www.luxena.com                                }
{      e-mail: support@luxena.com                           }
{                                                           }
{***********************************************************}

unit DBXQuery;

{$I lx.inc}

interface

uses
  Classes, DB, SqlExpr, LXMacroSQL, LXDataSetImpl, DBXDataSetImpl,
  DBXUpdateSQL, DBXSQLProcessor;


type

{ TDBXQuery }

  TDBXQuery = class(TSQLQuery, IDBXDataSetExtension, IDBXDataSetSupport)

  { Construction / Destruction }
  private
    FDataSetImpl: TDBXDataSetImpl;
    procedure SetDesignActivation(Value: Boolean);
    function GetDesignActivation: Boolean;
  protected
    procedure Loaded; override;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    procedure Prepare;
    procedure Unprepare;
  published
    property DesignActivation: Boolean read GetDesignActivation write SetDesignActivation default LX_DEFAULT_DESIGN_ACTIVATION;

  { Records Management }
  private
    procedure SetPageSize(Value: Integer);
    function GetPageSize: Integer;
    procedure SetPacketRecords(const Value: Integer);
    function GetPacketRecords: Integer;
    procedure SetCloseWithMaster(Value: Boolean);
    function GetCloseWithMaster: Boolean;
  protected
    procedure InternalOpen; override;
    procedure InternalClose; override;
    function IsCursorOpen: Boolean; override;
    function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  public
    procedure FetchAll;
    procedure Requery;
  published
    property PageSize: Integer read GetPageSize write SetPageSize default LX_DEFAULT_BUFFER_CHUNKS;
    property PacketRecords: Integer read GetPacketRecords write SetPacketRecords default LX_DEFAULT_PACKET_RECORDS;
    property CloseWithMaster: Boolean read GetCloseWithMaster write SetCloseWithMaster default DBX_DEFAULT_CLOSE_WITH_MASTER;

  { Buffer Management }
  private
    FNativeRead: Boolean;
  protected
    function AllocRecordBuffer: PChar; override;
    procedure FreeRecordBuffer(var Buffer: PChar); override;
    procedure SetBufListSize(Value: Integer); override; { restoring to TDataSet realization }
    procedure InitRecord(Buffer: PChar); override;
    procedure GetCalcFields(Buffer: PChar); override;
    procedure ClearCalcFields(Buffer: PChar); override;
  public
    function GetCurrentRecord(Buffer: PChar): Boolean; override;
  published
    property AutoCalcFields;

  { Field Management }
  protected
    function GetStateFieldValue(State: TDataSetState; Field: TField): Variant; override;
    procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  public
    function GetFieldData(Field: TField; Buffer: Pointer): Boolean; overload; override;
    function GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean; overload; override;
    function GetBlobFieldData(FieldNo: Integer; var Buffer: TBlobByteData): Integer; override;
    function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;

  { Navigation }
  private
    procedure SetUniDirectional(Value: Boolean);
    function GetUniDirectional: Boolean;
  protected
    procedure InternalFirst; override;
    procedure InternalLast; override;
  published
    property UniDirectional: Boolean read GetUniDirectional write SetUniDirectional default LX_DEFAULT_UNIDIRECTIONAL;

  { Editing }
  private
    procedure SetUpdateOptions(Value: TLXRecordUpdateOptions);
    function GetUpdateOptions: TLXRecordUpdateOptions;
    procedure SetUpdateMode(Value: TUpdateMode);
    function GetUpdateMode: TUpdateMode;
    procedure SetUpdateObject(Value: TLXDataSetUpdateObject);
    function GetUpdateObject: TLXDataSetUpdateObject;
    procedure SetRefreshLocates(Value: Boolean);
    function GetRefreshLocates: Boolean;
    procedure SetRecordRefreshMode(Value: TLXRecordRefreshMode);
    function GetRecordRefreshMode: TLXRecordRefreshMode;
    procedure SetCachedUpdates(Value: Boolean);
    function GetCachedUpdates: Boolean;
    procedure SetAutoCommitUpdates(Value: Boolean);
    function GetAutoCommitUpdates: Boolean;
  protected
    procedure InternalInitRecord(Buffer: PChar); override;
    procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
    procedure InternalInsert; override;
    procedure InternalEdit; override;
    procedure InternalDelete; override;
    procedure InternalPost; override;
    procedure InternalCancel; override;
    procedure InternalRefresh; override;
    function PSUpdateRecord(UpdateKind: TUpdateKind; Delta: TDataSet): Boolean; override;
  public
    procedure ApplyUpdates; overload;
    procedure ApplyUpdates(CommitUpdates: Boolean); overload;
    procedure CommitUpdates;
    procedure CancelUpdates;
    procedure RevertRecord;
  published
    property UpdateOptions: TLXRecordUpdateOptions read GetUpdateOptions write SetUpdateOptions default LX_DEFAULT_UPDATE_OPTIONS;
    property UpdateMode: TUpdateMode read GetUpdateMode write SetUpdateMode default LX_DEFAULT_UPDATE_MODE;
    property UpdateObject: TLXDataSetUpdateObject read GetUpdateObject write SetUpdateObject;
    property RefreshLocates: Boolean read GetRefreshLocates write SetRefreshLocates default LX_DEFAULT_REFRESH_LOCATES;
    property RecordRefreshMode: TLXRecordRefreshMode read GetRecordRefreshMode write SetRecordRefreshMode default LX_DEFAULT_RECORD_REFRESH_MODE;
    property CachedUpdates: Boolean read GetCachedUpdates write SetCachedUpdates default LX_DEFAULT_CACHED_UPDATES;
    property AutoCommitUpdates: Boolean read GetAutoCommitUpdates write SetAutoCommitUpdates default LX_DEFAULT_AUTO_COMMIT_UPDATES;

  { Informational }
  private
    function GetUpdatesPending: Boolean;
  protected
    function GetCanModify: Boolean; override;
    function GetRecordCount: Longint; override;
    procedure SetRecNo(Value: Integer); override;
    function GetRecNo: Integer; override;
    function GetRecordSize: Word; override;
  public
    function IsSequenced: Boolean; override;
    property UpdatesPending: Boolean read GetUpdatesPending;
    function UpdateStatus: TUpdateStatus; override;

  { Bookmarks Support }
  protected
    procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
    procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
    function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
    procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
    procedure InternalGotoBookmark(Bookmark: Pointer); override;
    procedure InternalSetToRecord(Buffer: PChar); override;
  public
    function BookmarkValid(Bookmark: TBookmark): Boolean; override;
    function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer; override;

  { Filter / Locate / Find }
  private
    procedure SetStatusFilter(Value: TUpdateStatusSet);
    function GetStatusFilter: TUpdateStatusSet;
  protected
    procedure SetFiltered(Value: Boolean); override;
    procedure SetOnFilterRecord(const Value: TFilterRecordEvent); override;
  public
    function Locate(const KeyFields: string; const KeyValues: Variant;
      Options: TLocateOptions): Boolean; override;
    function LocateNext(const KeyFields: string; const KeyValues: Variant;
      Options: TLocateOptions): Boolean; 
    function Lookup(const KeyFields: string; const KeyValues: Variant;
      const ResultFields: string): Variant; override;
    property StatusFilter: TUpdateStatusSet read GetStatusFilter write SetStatusFilter;
  published
    property Filtered;

  { Events }
  private
    procedure SetBeforeApplyUpdates(Value: TDataSetNotifyEvent);
    function GetBeforeApplyUpdates: TDataSetNotifyEvent;
    procedure SetAfterApplyUpdates(Value: TDataSetNotifyEvent);
    function GetAfterApplyUpdates: TDataSetNotifyEvent;
    procedure SetOnUpdateRecord(Value: TUpdateRecordEvent);
    function GetOnUpdateRecord: TUpdateRecordEvent;
    procedure SetOnGetTableName(Value: TLXGetTableNameEvent);
    function GetOnGetTableName: TLXGetTableNameEvent;
    procedure SetOnUpdateApply(Value: TDBXUpdateApplyEvent);
    function GetOnUpdateApply: TDBXUpdateApplyEvent;
    procedure SetOnUpdateApplied(Value: TLXUpdateAppliedEvent);
    function GetOnUpdateApplied: TLXUpdateAppliedEvent;
    procedure SetOnUpdateError(Value: TUpdateErrorEvent);
    function GetOnUpdateError: TUpdateErrorEvent;
    procedure SetOnRefreshRecord(Value: TDBXUpdateApplyEvent);
    function GetOnRefreshRecord: TDBXUpdateApplyEvent;
  published
    property BeforeInsert;
    property AfterInsert;
    property BeforeEdit;
    property AfterEdit;
    property BeforePost;
    property AfterPost;
    property BeforeCancel;
    property AfterCancel;
    property BeforeDelete;
    property AfterDelete;
    property OnDeleteError;
    property OnEditError;
    property OnFilterRecord;
    property OnNewRecord;
    property OnPostError;
    property BeforeApplyUpdates: TDataSetNotifyEvent read GetBeforeApplyUpdates write SetBeforeApplyUpdates;
    property AfterApplyUpdates: TDataSetNotifyEvent read GetAfterApplyUpdates write SetAfterApplyUpdates;
    property OnUpdateRecord: TUpdateRecordEvent read GetOnUpdateRecord write SetOnUpdateRecord;
    property OnGetTableName: TLXGetTableNameEvent read GetOnGetTableName write SetOnGetTableName;
    property OnUpdateApply: TDBXUpdateApplyEvent read GetOnUpdateApply write SetOnUpdateApply;
    property OnUpdateApplied: TLXUpdateAppliedEvent read GetOnUpdateApplied write SetOnUpdateApplied;
    property OnUpdateError: TUpdateErrorEvent read GetOnUpdateError write SetOnUpdateError;
    property OnRefreshRecord: TDBXUpdateApplyEvent read GetOnRefreshRecord write SetOnRefreshRecord;

  { Macro SQL support }
  private
    FSQLProcessor: TDBXSQLProcessor;
    FMacros: TStrings;
    FMacroSQLElement: TLXMacroSQLElement;
    procedure SetSQLProcessor(const Value: TDBXSQLProcessor);
    procedure MacroSQLChanged(Sender: TObject);
    procedure MacrosChanged(Sender: TObject);
    procedure SetMacros(const Value: TStrings);
    procedure InternalParseMacroSQL;
    procedure BuildCommandText;
    procedure CheckCommandText;
    function GetText: string;
  protected
    procedure OpenCursor(InfoQuery: Boolean); override;
  public
    procedure PrepareStatement; override;
    procedure ParseMacroSQL; 
    property Text read GetText;
  published
    property Macros: TStrings read FMacros write SetMacros;
    property SQLProcessor: TDBXSQLProcessor read FSQLProcessor write SetSQLProcessor;

  private
    function GetVersion: string;
    procedure SetVersion(const Value: string);
  published
    property Version: string read GetVersion write SetVersion stored False;

  { IDBXDataSetSupport }
  protected
    function CreateFieldManager(Field: TField): TLXDataFieldManager;
    function CustomDataSize: Integer;
    function FetchNext: TGetResult;
    function ReadField(Field: TField; Buffer: PChar): Boolean;
    procedure ReadCustomData(Buffer: PChar);
    procedure Restart;
    function GetActiveSQLProcessor: TDBXSQLProcessor;
    function GetMacros: TStrings;
  public
    property ActiveSQLProcessor: TDBXSQLProcessor read GetActiveSQLProcessor;

  end;


implementation

uses SysUtils, Variants{$IFDEF D10}, WideStrings{$ENDIF}, DBXConsts, DBXUtils,
  DBXEvents, DBXConnection;


{ TDBXQuery }

constructor TDBXQuery.Create(AOwner: TComponent);
begin
  inherited;
  FDataSetImpl := TDBXDataSetImpl.Create(Self, Self);
{$IFDEF D10}
  TWideStringList(SQL).OnChange := MacroSQLChanged;
{$ELSE}
  TStringList(SQL).OnChange := MacroSQLChanged;
{$ENDIF}
  FMacros := TStringList.Create;
  TStringList(FMacros).OnChange := MacrosChanged;
end;

destructor TDBXQuery.Destroy;
begin
  SetSQLProcessor(nil);
  FMacros.Free;
  FMacroSQLElement.Free;
  inherited;
  FDataSetImpl.Free;
end;

procedure TDBXQuery.AfterConstruction;
begin
  inherited;
  TDBXEvents.HandleObjectCreate(Self);
end;

procedure TDBXQuery.BeforeDestruction;
begin
  TDBXEvents.HandleObjectDestroy(Self);
  inherited;
end;

procedure TDBXQuery.Prepare;
begin
  Prepared := True;
end;

procedure TDBXQuery.Unprepare;
begin
  Prepared := False;
end;

procedure TDBXQuery.Loaded;
begin
  try
    CheckCommandText;
  except
    if csDesigning in ComponentState then
      InternalHandleException else
      raise;
  end;
  inherited;
end;

procedure TDBXQuery.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;
  if (AComponent = FSQLProcessor) and (Operation = opRemove) then
    FSQLProcessor := nil;
end;

procedure TDBXQuery.SetDesignActivation(Value: Boolean);
begin
  FDataSetImpl.DesignActivation := Value;
end;

function TDBXQuery.GetDesignActivation: Boolean;
begin
  Result := FDataSetImpl.DesignActivation;
end;

{ Records Management }

procedure TDBXQuery.SetPageSize(Value: Integer);
begin
  FDataSetImpl.BufferChunks := Value;
end;

function TDBXQuery.GetPageSize: Integer;
begin
  Result := FDataSetImpl.BufferChunks;
end;

procedure TDBXQuery.SetPacketRecords(const Value: Integer);
begin
  FDataSetImpl.PacketRecords := Value;
end;

function TDBXQuery.GetPacketRecords: Integer;
begin
  Result := FDataSetImpl.PacketRecords;
end;

procedure TDBXQuery.SetCloseWithMaster(Value: Boolean);
begin
  FDataSetImpl.CloseWithMaster := Value;
end;

function TDBXQuery.GetCloseWithMaster: Boolean;
begin
  Result := FDataSetImpl.CloseWithMaster;
end;

procedure TDBXQuery.InternalOpen;
begin
  inherited;
  FDataSetImpl.InternalOpen;
end;

procedure TDBXQuery.InternalClose;
begin
  FDataSetImpl.InternalClose;
  if CloseWithMaster then
    inherited else
  begin
    BindFields(False);
    if DefaultFields then DestroyFields;
    FreeBuffers;
    if Assigned(SQLConnection) and ((SQLConnection.KeepConnection) or
       (SQLConnection.DataSetCount > 1)) then
      FreeCursor else
      Prepared := False;
  end;
end;

function TDBXQuery.IsCursorOpen: Boolean;
begin
  Result := FDataSetImpl.IsCursorOpen or inherited IsCursorOpen;
end;

function TDBXQuery.GetRecord(Buffer: PChar; GetMode: TGetMode;
  DoCheck: Boolean): TGetResult;
begin
  CheckLicense;
  Result := FDataSetImpl.GetRecord(Buffer, GetMode, DoCheck);
end;

procedure TDBXQuery.FetchAll;
begin
  FDataSetImpl.FetchAll;
end;

procedure TDBXQuery.Requery;
begin
  FDataSetImpl.Requery;
end;

{ Buffer Management }

function TDBXQuery.AllocRecordBuffer: PChar;
begin
  Result := FDataSetImpl.AllocRecordBuffer;
end;

procedure TDBXQuery.FreeRecordBuffer(var Buffer: PChar);
begin
  FDataSetImpl.FreeRecordBuffer(Buffer);
end;

type
  TDataSetChild = class(TDataSet);
  
procedure TDBXQuery.SetBufListSize(Value: Integer); assembler;
asm
  call TDataSetChild.SetBufListSize;
end;

procedure TDBXQuery.InitRecord(Buffer: PChar);
begin
  FDataSetImpl.InitRecord(Buffer);
end;

procedure TDBXQuery.GetCalcFields(Buffer: PChar);
begin
  if FNativeRead then Exit;
  inherited;
end;

procedure TDBXQuery.ClearCalcFields(Buffer: PChar);
begin
  FDataSetImpl.ClearCalcFields(Buffer);
end;

function TDBXQuery.GetCurrentRecord(Buffer: PChar): Boolean;
begin
  Result := FDataSetImpl.GetCurrentRecord(Buffer);
end;

{ Field Management }

function TDBXQuery.GetStateFieldValue(State: TDataSetState; Field: TField): Variant;
begin
  if FDataSetImpl.HasFieldStateValue(State, Field) then
    Result := inherited GetStateFieldValue(State, Field) else
    Result := Unassigned;
end;

procedure TDBXQuery.SetFieldData(Field: TField; Buffer: Pointer);
begin
  FDataSetImpl.SetFieldData(Field, Buffer);
end;

function TDBXQuery.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
begin
  Result := FDataSetImpl.GetFieldData(Field, Buffer);
end;

function TDBXQuery.GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean;
begin
  Result := FDataSetImpl.GetFieldData(FieldNo, Buffer);
end;

function TDBXQuery.GetBlobFieldData(FieldNo: Integer; var Buffer: TBlobByteData): Integer;
begin
  Result := FDataSetImpl.GetBlobFieldData(FieldNo, Buffer);
end;

function TDBXQuery.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
begin
  Result := FDataSetImpl.CreateBlobStream(Field, Mode);
end;

{ Navigation }

procedure TDBXQuery.SetUniDirectional(Value: Boolean);
begin
  FDataSetImpl.UniDirectional := Value;
end;

function TDBXQuery.GetUniDirectional: Boolean;
begin
  Result := FDataSetImpl.UniDirectional;
end;

procedure TDBXQuery.InternalFirst;
begin
  FDataSetImpl.InternalFirst;
end;

procedure TDBXQuery.InternalLast;
begin
  FDataSetImpl.InternalLast;
end;

{ Editing }

procedure TDBXQuery.SetUpdateOptions(Value: TLXRecordUpdateOptions);
begin
  FDataSetImpl.UpdateOptions := Value;
end;

function TDBXQuery.GetUpdateOptions: TLXRecordUpdateOptions;
begin
  Result := FDataSetImpl.UpdateOptions;
end;

procedure TDBXQuery.SetUpdateMode(Value: TUpdateMode);
begin
  FDataSetImpl.UpdateMode := Value;
end;

function TDBXQuery.GetUpdateMode: TUpdateMode;
begin
  Result := FDataSetImpl.UpdateMode;
end;

procedure TDBXQuery.SetUpdateObject(Value: TLXDataSetUpdateObject);
begin
  FDataSetImpl.UpdateObject := Value;
end;

function TDBXQuery.GetUpdateObject: TLXDataSetUpdateObject;
begin
  Result := FDataSetImpl.UpdateObject;
end;

procedure TDBXQuery.SetRefreshLocates(Value: Boolean);
begin
  FDataSetImpl.RefreshLocates := Value;
end;

function TDBXQuery.GetRefreshLocates: Boolean;
begin
  Result := FDataSetImpl.RefreshLocates;
end;

procedure TDBXQuery.SetRecordRefreshMode(Value: TLXRecordRefreshMode);
begin
  FDataSetImpl.RecordRefreshMode := Value;
end;

function TDBXQuery.GetRecordRefreshMode: TLXRecordRefreshMode;
begin
  Result := FDataSetImpl.RecordRefreshMode;
end;

procedure TDBXQuery.SetCachedUpdates(Value: Boolean);
begin
  FDataSetImpl.CachedUpdates := Value;
end;

function TDBXQuery.GetCachedUpdates: Boolean;
begin
  Result := FDataSetImpl.CachedUpdates;
end;

procedure TDBXQuery.SetAutoCommitUpdates(Value: Boolean);
begin
  FDataSetImpl.AutoCommitUpdates := Value;
end;

function TDBXQuery.GetAutoCommitUpdates: Boolean;
begin
  Result := FDataSetImpl.AutoCommitUpdates;
end;

procedure TDBXQuery.InternalInitRecord(Buffer: PChar);
begin
  FDataSetImpl.InternalInitRecord(Buffer);
end;

procedure TDBXQuery.InternalAddRecord(Buffer: Pointer; Append: Boolean);
begin
  FDataSetImpl.InternalAddRecord(Buffer, Append);
end;

procedure TDBXQuery.InternalInsert;
begin
  FDataSetImpl.InternalInsert;
end;

procedure TDBXQuery.InternalEdit;
begin
  FDataSetImpl.InternalEdit;
end;

procedure TDBXQuery.InternalDelete;
begin
  FDataSetImpl.InternalDelete;
end;

procedure TDBXQuery.InternalPost;
begin
  inherited;
  FDataSetImpl.InternalPost;
end;

procedure TDBXQuery.InternalCancel;
begin
  FDataSetImpl.InternalCancel;
end;

procedure TDBXQuery.InternalRefresh;
begin
  FDataSetImpl.InternalRefresh;
end;

function TDBXQuery.PSUpdateRecord(UpdateKind: TUpdateKind; Delta: TDataSet): Boolean;
begin
  Result := FDataSetImpl.UpdateRecord(UpdateKind, Delta);
end;

procedure TDBXQuery.ApplyUpdates;
begin
  FDataSetImpl.ApplyUpdates;
end;

procedure TDBXQuery.ApplyUpdates(CommitUpdates: Boolean);
begin
  FDataSetImpl.ApplyUpdates(CommitUpdates);
end;

procedure TDBXQuery.CommitUpdates;
begin
  FDataSetImpl.CommitUpdates;
end;

procedure TDBXQuery.CancelUpdates;
begin
  FDataSetImpl.CancelUpdates;
end;

procedure TDBXQuery.RevertRecord;
begin
  FDataSetImpl.RevertRecord;
end;

{ Informational }

function TDBXQuery.GetCanModify: Boolean;
begin
  Result := FDataSetImpl.GetCanModify;
end;

function TDBXQuery.GetUpdatesPending: Boolean;
begin
  Result := FDataSetImpl.UpdatesPending;
end;

function TDBXQuery.GetRecordCount: Longint;
begin
  Result := FDataSetImpl.GetRecordCount;
end;

procedure TDBXQuery.SetRecNo(Value: Integer);
begin
  FDataSetImpl.SetRecNo(Value);
end;

function TDBXQuery.GetRecNo: Integer;
begin
  Result := FDataSetImpl.GetRecNo;
end;

function TDBXQuery.GetRecordSize: Word;
begin
  Result := FDataSetImpl.GetRecordSize;
end;

function TDBXQuery.IsSequenced: Boolean;
begin
  Result := FDataSetImpl.IsSequenced;
end;

function TDBXQuery.UpdateStatus: TUpdateStatus;
begin
  Result := FDataSetImpl.UpdateStatus;
end;

{ Bookmarks Support }

procedure TDBXQuery.GetBookmarkData(Buffer: PChar; Data: Pointer);
begin
  FDataSetImpl.GetBookmarkData(Buffer, Data);
end;

procedure TDBXQuery.SetBookmarkData(Buffer: PChar; Data: Pointer);
begin
  FDataSetImpl.SetBookmarkData(Buffer, Data);
end;

function TDBXQuery.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
begin
  Result := FDataSetImpl.GetBookmarkFlag(Buffer);
end;

procedure TDBXQuery.SetBookmarkFlag(Buffer: PChar;
  Value: TBookmarkFlag);
begin
  FDataSetImpl.SetBookmarkFlag(Buffer, Value);
end;

procedure TDBXQuery.InternalGotoBookmark(Bookmark: Pointer);
begin
  FDataSetImpl.InternalGotoBookmark(Bookmark);
end;

procedure TDBXQuery.InternalSetToRecord(Buffer: PChar);
begin
  FDataSetImpl.InternalSetToRecord(Buffer);
end;

function TDBXQuery.BookmarkValid(Bookmark: TBookmark): Boolean;
begin
  Result := FDataSetImpl.BookmarkValid(Bookmark);
end;

function TDBXQuery.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer;
begin
  Result := FDataSetImpl.CompareBookmarks(Bookmark1, Bookmark2);
end;

{ Filter / Locate / Find }

procedure TDBXQuery.SetFiltered(Value: Boolean);
begin
  if Value <> Filtered then
  begin
    inherited SetFiltered(Value);
    if Active and Assigned(OnFilterRecord) then First;
  end;
end;

procedure TDBXQuery.SetOnFilterRecord(const Value: TFilterRecordEvent);
begin
  if @Value <> @OnFilterRecord then
  begin
    inherited SetOnFilterRecord(Value);
    if Active and Filtered then First;
  end;
end;

procedure TDBXQuery.SetStatusFilter(Value: TUpdateStatusSet);
begin
  FDataSetImpl.StatusFilter := Value;
end;

function TDBXQuery.GetStatusFilter: TUpdateStatusSet;
begin
  Result := FDataSetImpl.StatusFilter;
end;

function TDBXQuery.Locate(const KeyFields: string;
  const KeyValues: Variant; Options: TLocateOptions): Boolean;
begin
  Result := FDataSetImpl.Locate(KeyFields, KeyValues, Options, True);
end;

function TDBXQuery.LocateNext(const KeyFields: string;
  const KeyValues: Variant; Options: TLocateOptions): Boolean;
begin
  Result := FDataSetImpl.Locate(KeyFields, KeyValues, Options, False);
end;

function TDBXQuery.Lookup(const KeyFields: string;
  const KeyValues: Variant; const ResultFields: string): Variant;
begin
  Result := FDataSetImpl.Lookup(KeyFields, KeyValues, ResultFields);
end;

procedure TDBXQuery.SetBeforeApplyUpdates(Value: TDataSetNotifyEvent);
begin
  FDataSetImpl.BeforeApplyUpdates := Value;
end;

function TDBXQuery.GetBeforeApplyUpdates: TDataSetNotifyEvent;
begin
  Result := FDataSetImpl.BeforeApplyUpdates;
end;

procedure TDBXQuery.SetAfterApplyUpdates(Value: TDataSetNotifyEvent);
begin
  FDataSetImpl.AfterApplyUpdates := Value;
end;

function TDBXQuery.GetAfterApplyUpdates: TDataSetNotifyEvent;
begin
  Result := FDataSetImpl.AfterApplyUpdates;
end;

procedure TDBXQuery.SetOnUpdateRecord(Value: TUpdateRecordEvent);
begin
  FDataSetImpl.OnUpdateRecord := Value;
end;

function TDBXQuery.GetOnUpdateRecord: TUpdateRecordEvent;
begin
  Result := FDataSetImpl.OnUpdateRecord;
end;

procedure TDBXQuery.SetOnGetTableName(Value: TLXGetTableNameEvent);
begin
  FDataSetImpl.OnGetTableName := Value;
end;

function TDBXQuery.GetOnGetTableName: TLXGetTableNameEvent;
begin
  Result := FDataSetImpl.OnGetTableName;
end;

procedure TDBXQuery.SetOnUpdateApply(Value: TDBXUpdateApplyEvent);
begin
  FDataSetImpl.OnUpdateApply := Value;
end;

function TDBXQuery.GetOnUpdateApply: TDBXUpdateApplyEvent;
begin
  Result := FDataSetImpl.OnUpdateApply;
end;

procedure TDBXQuery.SetOnUpdateApplied(Value: TLXUpdateAppliedEvent);
begin
  FDataSetImpl.OnUpdateApplied := Value;
end;

function TDBXQuery.GetOnUpdateApplied: TLXUpdateAppliedEvent;
begin
  Result := FDataSetImpl.OnUpdateApplied;
end;

procedure TDBXQuery.SetOnUpdateError(Value: TUpdateErrorEvent);
begin
  FDataSetImpl.OnUpdateError := Value;
end;

function TDBXQuery.GetOnUpdateError: TUpdateErrorEvent;
begin
  Result := FDataSetImpl.OnUpdateError;
end;

procedure TDBXQuery.SetOnRefreshRecord(Value: TDBXUpdateApplyEvent);
begin
  FDataSetImpl.OnRefreshRecord := Value;
end;

function TDBXQuery.GetOnRefreshRecord: TDBXUpdateApplyEvent;
begin
  Result := FDataSetImpl.OnRefreshRecord;
end;

{ Macro SQL support }

procedure TDBXQuery.SetSQLProcessor(const Value: TDBXSQLProcessor);
begin
  if Value <> FSQLProcessor then
  begin
    if FSQLProcessor <> nil then
      FSQLProcessor.RemoveFreeNotification(Self);
    FSQLProcessor := Value;
    if FSQLProcessor <> nil then
      FSQLProcessor.FreeNotification(Self);
  end;
end;

procedure TDBXQuery.MacroSQLChanged(Sender: TObject);
begin
  if not (csReading in ComponentState) then
    try
      Close;
      Prepared := False;
      InternalParseMacroSQL;
    except
    end;
end;

procedure TDBXQuery.MacrosChanged(Sender: TObject);
begin
  if FMacroSQLElement <> nil then
    try
      Close;
      Prepared := False;
      BuildCommandText;
    except
      SetFCommandText('');
    end;
end;

procedure TDBXQuery.SetMacros(const Value: TStrings);
begin
  FMacros.Assign(Value);
end;

procedure TDBXQuery.InternalParseMacroSQL;
begin
  FreeAndNil(FMacroSQLElement);
  if not ParamCheck then
    SetFCommandText(SQL.Text) else
  begin
    SetFCommandText('');
    FMacroSQLElement := ActiveSQLProcessor.Parse(SQL.Text);
    BuildCommandText;
  end;
end;

procedure TDBXQuery.BuildCommandText;

  procedure UpdateParams(ParamNames: TStrings);
  var
    TmpParams: TParams;
    I: Integer;
  begin
    if ParamNames.Count = 0 then
      Params.Clear else
    begin
      TmpParams := TParams.Create;
      try
        for I := 0 to ParamNames.Count - 1 do
          TmpParams.CreateParam(ftUnknown, ParamNames[I], ptInput);
        TmpParams.AssignValues(Params);
        Params.Assign(TmpParams);
      finally
        TmpParams.Free;
      end;
    end;
  end;

var
  MacroSQLResult: ILXMacroSQLResult;
begin
  MacroSQLResult := ActiveSQLProcessor.Build(FMacroSQLElement, FMacros);
  SetFCommandText(MacroSQLResult.SQL);
  UpdateParams(MacroSQLResult.ParamNames);
  DataEvent(dePropertyChange, 0);
end;

procedure TDBXQuery.CheckCommandText;
begin
  if (CommandText = '') and (SQL.Text <> '') then
    if FMacroSQLElement = nil then
      InternalParseMacroSQL else
      BuildCommandText;
end;

function TDBXQuery.GetText: string;
begin
  Result := CommandText;
end;

procedure TDBXQuery.OpenCursor(InfoQuery: Boolean);
begin
  CheckCommandText;
  inherited;
end;

procedure TDBXQuery.PrepareStatement;
begin
  CheckCommandText;
  inherited;
end;

procedure TDBXQuery.ParseMacroSQL;
begin
  Close;
  Prepared := False;
  InternalParseMacroSQL;
end;

function TDBXQuery.GetVersion: string;
begin
  Result := DBXVersion;
end;

procedure TDBXQuery.SetVersion(const Value: string);
begin
end;

{ TDBXQuery.IDBXDataSetSupport }

function TDBXQuery.CreateFieldManager(Field: TField): TLXDataFieldManager;
begin
  if Field.IsBlob then
    Result := TLXBlobFieldManager.Create(Field) else
    Result := TLXDataFieldManager.Create(Field);
end;

function TDBXQuery.CustomDataSize: Integer;
begin
  Result := 0;
end;

function TDBXQuery.FetchNext: TGetResult;
var
  EofBeforeCall: Boolean;
begin
  FNativeRead := True;
  try
    EofBeforeCall := EOF;
    Result := inherited GetRecord(nil, gmNext, False);
    if EofBeforeCall and not EOF then
      ClearBuffers;
  finally
    FNativeRead := False;
  end;
  if Result = grOK then
    TDBXDataSetImpl.SetEof(Self, False) else
    FreeCursor;
end;

function TDBXQuery.ReadField(Field: TField; Buffer: PChar): Boolean;
var
  ArrayBuf: TBlobByteData;
begin
  if not Field.IsBlob then
    Result := inherited GetFieldData(Field.FieldNo, Buffer) else
  begin
    Result := inherited GetBlobFieldData(Field.FieldNo, ArrayBuf) > 0;
    if Result then
    begin
      SetLength(PLXBlobData(Buffer)^, Length(ArrayBuf));
      Move(Pointer(ArrayBuf)^, PChar(PLXBlobData(Buffer)^)^, Length(ArrayBuf));
    end;
  end;
end;

procedure TDBXQuery.ReadCustomData(Buffer: PChar);
begin
end;

procedure TDBXQuery.Restart;
begin
  FreeCursor;
  ExecuteStatement;
end;

function TDBXQuery.GetActiveSQLProcessor: TDBXSQLProcessor;
begin
  Result := FSQLProcessor;
  if Result = nil then
    if SQLConnection is TDBXConnection then
      Result := TDBXConnection(SQLConnection).ActiveSQLProcessor else
      Result := TDBXSQLProcessor.DefaultProcessor;
end;

function TDBXQuery.GetMacros: TStrings;
begin
  Result := FMacros;
end;


end.